package com.bilibili.server.service;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.bilibili.autoconfig.template.HuanXinTemplate;
import com.bilibili.autoconfig.template.TXSMSTemplate;
import com.bilibili.commons.Constants;
import com.bilibili.commons.exception.TanHuaException;
import com.bilibili.commons.utils.JwtUtils;
import com.bilibili.dubbo.api.db.UserApi;
import com.bilibili.model.vo.ErrorResult;
import com.bilibili.model.db.User;
import com.bilibili.server.interceptor.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author HuangRongFu
 * 2021/11/16 星期二8:22 上午
 */


//该类 只在发送验证码时使用 所以不用写成接口 如果后续有其他服务需要调用 可以将类改写为接口
@Service
@Slf4j
public class UserService {

    @Autowired //要从Redis中获取值 要使用Redis在spring中提供的对象  因为要获取的是字符串 所以使用StringRedisTemplate
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private TXSMSTemplate txsmsTemplate;

    @Autowired
    private HuanXinTemplate huanXinTemplate;

    @Autowired
    private UserFreezeService userFreezeService;

    @Autowired
    private MqMessageService mqMessageService;

    @DubboReference
    private UserApi userApi;

    /**
     * 发送短信验证码
     * @param: paramMap
     */
    public void sendValidateCode(Map<String, String> paramMap) {

        String phone = paramMap.get("phone");

        if ( ! phone.matches("[1][0-9]{10}")) {

            throw new TanHuaException(Constants.PHONE_ERROR);
        }

        userFreezeService.checkUserStatus(1, phone);

        //1. 从redis取出验证码
        String key = "VALIDATE_CODE_" + phone;

        //2. 有值
        String codeInRedis = stringRedisTemplate.opsForValue().get(key);
        log.info("redis中的验证码有{},{}", phone, codeInRedis);

        // 2.1 验证码未失效，注意查收
        if (StringUtils.isNotEmpty(codeInRedis)) {

            throw new TanHuaException(ErrorResult.duplicate());
        }

        // 3. 没值
        // 3.1 生成验证码
        //String validateCode = RandomStringUtils.randomNumeric(6);
        String validateCode = "123456"; // 模拟发送的验证码
        // 3.2 发送验证码
        //txsmsTemplate.sendValidateCode(phone,validateCode);
        // 3.3 存入redis 设置有效期为5分钟
        stringRedisTemplate.opsForValue().set(key, validateCode, 3, TimeUnit.DAYS);
    }

    /**
     * 校验验证码
     * @params: paramMap
     * @Return: ResponseEntity
     */
    public Map<String, Object> loginVerification(@RequestBody Map<String, String> paramMap) {

        //1.取出手机号码
        String phone = paramMap.get("phone");

        //前端输入的验证码
        String verificationCode = paramMap.get("verificationCode");

        //2.拼接key(必须与存入时的key一致)
        String key = "VALIDATE_CODE_" + phone;

        check(phone, verificationCode, key);

        //7.校验成功后
        //8.判断手机号码是否存在, 通过手机号码查询数据库(mysql.tb_user)
        User loginUser = userApi.findByPhone(phone);

        //9.不存在，
        Boolean isNew = false;
        if (loginUser == null) {

            //9.1就注册为新用户
            loginUser = new User();

            //添加数据到数据库(tb_user)
            loginUser.setMobile(phone);
            //取用户手机号后六位作为密码  然后用md5进行加密 (mobile:phone, password:手机号后6位 加密md5,new Date(),new Date())
            loginUser.setPassword(SecureUtil.md5(phone.substring(5)));

            //添加用户后获取自增的id
            Long userId = userApi.add(loginUser);

            //设置用户id
            loginUser.setId(userId);

            String hxUser = "hx" + loginUser.getId();
            Boolean creat = huanXinTemplate.createUser(hxUser, loginUser.getPassword());

            if(creat){

                loginUser.setHxUser(hxUser);
                loginUser.setHxPassword(Constants.INIT_PASSWORD);
                userApi.update(loginUser);
            }

            isNew = true ;

            mqMessageService.sendLogMessage(userId, "0102", "user", null);

        }

        //10.登录成功 构建要返回的数据map
        Map<String, Object> resultMap = new HashMap<>();

        //要传入token中的map集合
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("phone",phone);
        tokenMap.put("id", loginUser.getId().toString());

        //token的创建   存入token的数据 为了方便后续的方法调用  直接将loginUser转为jsonString 存入token
        String token = JwtUtils.getToken(tokenMap);
        String loginUserJson = JSON.toJSONString(loginUser);

        String tokenKey = "TOKEN_" + token;
        //token存入redis  有效期为三天
        stringRedisTemplate.opsForValue().set(tokenKey, loginUserJson, 3 ,TimeUnit.DAYS);

        resultMap.put("token", token);
        resultMap.put("isNew",isNew);

        mqMessageService.sendLogMessage(loginUser.getId(), "0101", "user", null);

        //11.再返回给controller
        return resultMap;
    }

    public void check(String phone, String verificationCode, String key){

        //3.取redis中的验证码
        String codeInRedis = stringRedisTemplate.opsForValue().get(key);

        log.info("redis中的验证码有{},{},{}", phone, codeInRedis, verificationCode);

        //4.看过时没有
        //5.过时了，redis中的验证码为null
        if (StringUtils.isEmpty(codeInRedis)) {

            throw new TanHuaException(ErrorResult.loginError());
        }

        //6.进行与前端传过来的验证码比较
        if (!StringUtils.equals(codeInRedis, verificationCode)) {

            throw new TanHuaException(ErrorResult.validateCodeError());
        }

        //验证成功删除key 防止重复提交
        stringRedisTemplate.delete(key);
    }
}
